#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "CommandProcessor.h"

#include "Class.h"
#include "Command.h"
#include "ObjArray.h"
#include "Menu.h"
#include "CmdNo.h"

//---- CommandProcessor --------------------------------------------------------

NewMetaImpl(CommandProcessor,RefCounted, (T(changeCount), T(cap)));

CommandProcessor::CommandProcessor()
{
	cap= changeCount= 0;
}

CommandProcessor::~CommandProcessor()
{
	//if (changeCount > 0)
	//    Error("~CommandProcessor", "changeCount > 0");
}

Command *CommandProcessor::GetAt(int)
{
	return 0;
}

void CommandProcessor::PerformCommand(Command* cmd)
{
	if (cmd == 0)
		return;

	switch (cmd->GetType()) {

	case eCmdTypeCantUndo:
		FinishFrom(0);
		changeCount+= cmd->Perform();
		Changed();
		cmd->Done(0);
		cmd->Finish();
		break;

	case eCmdTypeNoChange:
		CommandProcessor::PerformNormalCommand(cmd);
		break;

	case eCmdTypeNormal:
		FinishFrom(changeCount);
		PerformNormalCommand(cmd);
		break;
	}
}

void CommandProcessor::PerformNormalCommand(Command* cmd)
{
	if (cmd) {
		cmd->Perform();
		cmd->Done(0);
		cmd->Finish();
	}
}

void CommandProcessor::DoCreateMenu(Menu *menu)
{
	if (menu->GetId() == cEDITMENU) {
		if (Size() <= 0)
			menu->AppendItems("Can't undo2", cUNDO, 0);
		else {
			menu->AppendItem("Undo2@Z", cUNDO);
			if (Size() >= 2)
				menu->AppendItems("Redo2@Z", cREDO, 0);
		}
	}
}

void CommandProcessor::DoSetupMenu(Menu *menu)
{
	if (menu->GetId() == cEDITMENU) {
		Command *cmd;
		
		if (cmd= GetAt(changeCount-1)) {
			menu->ReplaceItem(cUNDO, cmd->GetUndoName());
			if (cmd->GetType() != eCmdTypeCantUndo)
				menu->EnableItem(cUNDO);
		} else {
			menu->ReplaceItem(cUNDO, "Undo@Z");
			menu->DisableItem(cUNDO);
		}
		
		if (Size() >= 2) {
			if (cmd= GetAt(changeCount)) {
				menu->ReplaceItem(cREDO, cmd->GetUndoName());
				menu->EnableItem(cREDO);
			} else {
				menu->ReplaceItem(cREDO, "Redo@Z");
				menu->DisableItem(cREDO);
			}
		}
	}
}

void CommandProcessor::DoMenuCommand(int cmdId)
{
	Command *c;

	switch (cmdId) {
	case cUNDO:
		if (c= GetAt(changeCount-1)) {
			changeCount+= c->Perform();
			Changed();
		}
		break;
	case cREDO:
		if (c= GetAt(changeCount)) {
			changeCount+= c->Perform();
			Changed();
		}
		break;
	}
}

void CommandProcessor::Finish()
{
	FinishFrom(0);
	changeCount= 0;
	Changed();
}

void CommandProcessor::FinishFrom(int)
{
}

//---- SingleCommandProcessor --------------------------------------------------

NewMetaImpl(SingleCommandProcessor,CommandProcessor, (TP(lastCmd)));

SingleCommandProcessor::SingleCommandProcessor()
{
	lastCmd= 0;
	cap= 1;
}

Command *SingleCommandProcessor::GetAt(int)
{
	return lastCmd;
}

void SingleCommandProcessor::PerformNormalCommand(Command* cmd)
{
	if (lastCmd)
		lastCmd->Done(cmd);
	lastCmd= cmd;
	changeCount+= cmd->Perform();
	Changed();
}

void SingleCommandProcessor::FinishFrom(int)
{
	if (lastCmd) {
		lastCmd->Finish();
		lastCmd= 0;
	}
}

//---- UnboundedCommandProcessor -----------------------------------------------

NewMetaImpl(UnboundedCommandProcessor,CommandProcessor, (TP(cmdHistory)));

UnboundedCommandProcessor::UnboundedCommandProcessor()
{
	cmdHistory= new ObjArray;
	cap= 99999;
}

UnboundedCommandProcessor::~UnboundedCommandProcessor()
{
	SafeDelete(cmdHistory);
}

Command *UnboundedCommandProcessor::GetAt(int i)
{
	if (i >= 0 && i < cmdHistory->Size())
		return (Command*) cmdHistory->At(i);
	return 0;
}

void UnboundedCommandProcessor::PerformNormalCommand(Command* cmd)
{
	Command *lastcmd;
	if (lastcmd= GetAt(changeCount-1))
		lastcmd->Done(cmd);
	cmdHistory->AtPutAndExpand(changeCount, cmd);
	changeCount+= cmd->Perform();
	Changed();
}

void UnboundedCommandProcessor::FinishFrom(int n)
{
	for (int i= n; i < cmdHistory->Size(); i++) {
		Command *c= (Command*) cmdHistory->At(i);
		if (c) {
			c->Finish();
			cmdHistory->RemoveAt(i);
		}
	}
}

//---- BoundedCommandProcessor -------------------------------------------------

NewMetaImpl(BoundedCommandProcessor,CommandProcessor, (TVP(mem,cap), T(start), T(size)));

BoundedCommandProcessor::BoundedCommandProcessor(int s)
{
	cap= s;
	mem= new Command* [cap];
	size= start= 0;
}

BoundedCommandProcessor::~BoundedCommandProcessor()
{
	SafeDelete(mem);
}

Command *BoundedCommandProcessor::GetAt(int i)
{
	if (i >= start && i < start+size)
		return mem[i % size];
	return 0;
}

void BoundedCommandProcessor::PerformNormalCommand(Command* cmd)
{
	Command *lastcmd;
	if (size >= cap) {      // overflow
		start++;
		GetAt(changeCount)->Finish();
		size--;
	}
	size++;
	if (lastcmd= GetAt(changeCount-1))
		lastcmd->Done(cmd);
	mem[changeCount % size]= cmd;
	changeCount+= cmd->Perform();
	Changed();
}

void BoundedCommandProcessor::FinishFrom(int n)
{
	Command *c;

	n= (n == 0) ? start : changeCount;

	for (int i= n; c= GetAt(i); i++)
		c->Finish();
	size-= (i-changeCount);
}

